/* * (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> * * $Id$ */ package org.nuxeo.ecm.platform.ui.web.tag.handler; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.el.ELException; import javax.faces.FacesException; import javax.faces.component.UIComponent; import javax.faces.view.facelets.ComponentConfig; import javax.faces.view.facelets.ComponentHandler; import javax.faces.view.facelets.FaceletContext; import javax.faces.view.facelets.FaceletException; import javax.faces.view.facelets.FaceletHandler; import javax.faces.view.facelets.TagAttribute; import javax.faces.view.facelets.TagConfig; import javax.faces.view.facelets.TagHandler; import org.apache.commons.lang.StringUtils; import com.sun.faces.facelets.component.UIRepeat; import com.sun.faces.facelets.tag.TagAttributeImpl; import com.sun.faces.facelets.tag.TagAttributesImpl; import com.sun.faces.facelets.tag.jstl.core.ForEachHandler; /** * Repeat handler, similar to the standard ForEach handler. * <p> * This component encapsulates a c:forEach tag inside a nxu:set tag, to be able to control when the sub-components * should be re-created in the case of an ajax re-render. * * @author Anahide Tchertchian */ public class RepeatTagHandler extends TagHandler { /** * @since 5.7 */ protected static final String ITERATION_VAR_PREFIX = "nxuRepeat_"; /** * @since 5.7 */ protected final TagConfig config; /** * @deprecated, user {@link #items} instead */ @Deprecated protected final TagAttribute value; /** * @since 5.7 */ protected final TagAttribute items; /** * @since 5.7 */ protected final TagAttribute itemsId; protected final TagAttribute var; protected final TagAttribute index; /** * @since 5.7 */ protected final TagAttribute status; /** * @since 5.7 */ protected final TagAttribute begin; /** * @since 5.7 */ protected final TagAttribute end; /** * @since 5.7 */ protected final TagAttribute step; /** * @since 5.7 */ protected final TagAttribute tranzient; /** * @since 5.7 */ protected final TagAttribute varStatus; /** * @since 8.2 */ protected final TagAttribute id; /** * @since 8.2 */ protected final TagAttribute renderTime; public RepeatTagHandler(TagConfig config) { super(config); this.config = config; id = getAttribute("id"); items = getAttribute("items"); itemsId = getAttribute("itemsId"); value = getAttribute("value"); var = getAttribute("var"); index = getAttribute("index"); status = getAttribute("status"); begin = getAttribute("begin"); end = getAttribute("end"); step = getAttribute("step"); tranzient = getAttribute("transient"); varStatus = getAttribute("varStatus"); renderTime = getAttribute("renderTime"); } protected TagAttribute getItemsAttribute() { TagAttribute itemsAttr = items; if (items == null) { // BBB itemsAttr = value; } return itemsAttr; } protected String getTagConfigId(FaceletContext ctx) { String refId = null; if (itemsId != null) { refId = itemsId.getValue(ctx); } if (StringUtils.isBlank(refId)) { TagAttribute itemsAttr = getItemsAttribute(); Object val = null; if (itemsAttr != null) { val = itemsAttr.getObject(ctx); if (val != null) { refId = val.toString(); } } } StringBuilder builder = new StringBuilder(); if (refId != null) { builder.append(refId); } builder.append(";"); Integer intValue = new Integer(builder.toString().hashCode()); return intValue.toString(); } /** * Encapsulate the call to a c:forEach tag in an SetTagHandler exposing the value and making sure the tagConfigId * changes when the value changes (see NXP-11434). * <p> * See also NXP-15050: since 6.0, anchor handler in component tree to ensure proper ajax refresh when iteration * value changes. */ @Override public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException, ELException { String anchor = String.valueOf(true); FaceletHandler nextHandler = this.nextHandler; TagAttribute varStatusAttr = varStatus; if (index != null) { // wrap the next handler in a set tag handler to expose the index // value from the varStatus attribute. String indexValue = index.getValue(ctx); if (!StringUtils.isBlank(indexValue)) { String varStatusValue = varStatus != null ? varStatus.getValue(ctx) : null; if (StringUtils.isBlank(varStatusValue)) { // need to create and set it as an attribute for the index // to be exposed varStatusAttr = createAttribute(config, "varStatus", getVarName(indexValue + "_varStatus")); } else { varStatusAttr = createAttribute(config, "varStatus", varStatusValue); } ComponentConfig indexVarConfig = TagConfigFactory.createAliasTagConfig(config, tagId, indexValue, "#{" + varStatusAttr.getValue() + ".index}", "false", anchor, this.nextHandler); nextHandler = new SetTagHandler(indexVarConfig); } } FaceletHandler handler; if (renderTime(ctx)) { List<TagAttribute> repeatAttrs = new ArrayList<TagAttribute>(); TagAttribute itemsAttr = getItemsAttribute(); repeatAttrs.add(createAttribute(config, "value", itemsAttr != null ? itemsAttr.getValue() : null)); repeatAttrs.addAll(copyAttributes(config, id, var, begin, end, step, varStatusAttr, tranzient)); ComponentConfig repeatConfig = TagConfigFactory.createComponentConfig(config, tagId, new TagAttributesImpl(repeatAttrs.toArray(new TagAttribute[] {})), nextHandler, UIRepeat.COMPONENT_TYPE, null); handler = new ComponentHandler(repeatConfig); } else { List<TagAttribute> forEachAttrs = new ArrayList<TagAttribute>(); forEachAttrs.add(createAttribute(config, "items", "#{" + getVarName("items") + "}")); forEachAttrs.addAll(copyAttributes(config, var, begin, end, step, varStatusAttr, tranzient)); TagConfig forEachConfig = TagConfigFactory.createTagConfig(config, tagId, new TagAttributesImpl(forEachAttrs.toArray(new TagAttribute[] {})), nextHandler); ForEachHandler forEachHandler = new ForEachHandler(forEachConfig); String setTagConfigId = getTagConfigId(ctx); TagAttribute itemsAttr = getItemsAttribute(); ComponentConfig aliasConfig = TagConfigFactory.createAliasTagConfig(config, setTagConfigId, getVarName("items"), itemsAttr != null ? itemsAttr.getValue() : null, "false", anchor, forEachHandler); handler = new SetTagHandler(aliasConfig); } // apply handler.apply(ctx, parent); } protected boolean renderTime(FaceletContext ctx) { if (renderTime != null) { return renderTime.getBoolean(ctx); } return false; } protected String getVarName(String id) { return ITERATION_VAR_PREFIX + id; } protected TagAttribute createAttribute(TagConfig tagConfig, String name, String value) { return new TagAttributeImpl(tagConfig.getTag().getLocation(), "", name, name, value); } protected TagAttribute copyAttribute(TagConfig tagConfig, TagAttribute attribute) { return new TagAttributeImpl(tagConfig.getTag().getLocation(), "", attribute.getLocalName(), attribute.getLocalName(), attribute.getValue()); } protected List<TagAttribute> copyAttributes(TagConfig tagConfig, TagAttribute... attributes) { List<TagAttribute> res = new ArrayList<TagAttribute>(); if (attributes != null) { for (TagAttribute attr : attributes) { if (attr != null) { res.add(copyAttribute(tagConfig, attr)); } } } return res; } }